import { common } from '@/exports/utils'
import { existsSync, existToMkdirSync, logger, requireFileSync, watch, writeJsonSync } from 'node-karin'
import lodash from 'node-karin/lodash'
import path from 'node:path'
import { EnhancedArray } from './array'

export class Config<C extends Record<string, any>> {
  #cfgName: `${string}:${string}`
  /**
   * @description 配置缓存
   */
  #ConfigCache: C | null = null
  /**
   * @description 默认配置
   */
  #DefaultConfig: C
  #DefineConfig: Record<string, any>
  /**
   * @description 配置保存路径
   */
  #ConfigPath: string

  /**
   * @param name 插件名称:配置名称
   */
  constructor (name: `${string}:${string}`, ConfigDir: string, DefaultConfig: C, DefineConfig: Record<string, any>) {
    this.#cfgName = name

    const splitName = name.split(':')
    if (!splitName[1]) {
      throw new Error('配置名称格式错误，应为 插件名称:配置名称')
    }

    this.#ConfigPath = path.join(ConfigDir, `${splitName[1]}.json`)

    this.#DefaultConfig = DefaultConfig
    this.#DefineConfig = DefineConfig

    existToMkdirSync(ConfigDir)

    !existsSync(this.#ConfigPath) && writeJsonSync(this.#ConfigPath, DefaultConfig, true)

    this.loadConfig()
  }

  loadConfig (): C {
    const config = requireFileSync(this.#ConfigPath)

    // 检查并补全缺失的配置项
    const mergedConfig = this.mergeWithDefaults(config, this.#DefaultConfig, this.#DefineConfig)

    // 更新缓存
    this.#ConfigCache = mergedConfig

    return mergedConfig
  }

  mergeWithDefaults (userConfig: C, defaultConfig: C, DefineConfig: Record<string, any>): C {
    // 先过滤用户配置，只保留默认配置中定义的字段
    const filteredUserConfig = common.filterData(userConfig, defaultConfig, DefineConfig)

    // 然后合并配置
    const result = lodash.merge({}, defaultConfig, filteredUserConfig)

    if (!lodash.isEqual(result, userConfig)) {
      try {
        writeJsonSync(this.#ConfigPath, result)
      } catch (err) {
        logger.error(err)
      }
    }

    return result
  }

  /**
   * @description 获取配置路径对应的默认配置
   */
  getDef<T> (path: string) {
    const defConfig = JSON.parse(JSON.stringify(this.#DefaultConfig))

    return lodash.get(defConfig, path) as T
  }

  /**
   * @description 获取配置路径对应的配置
   */
  get<T> (path: string, isArray?: false, def?: T): T
  get<T> (path: string, isArray: true, def?: T[]): EnhancedArray<T>
  get<T> (path: string, isArray: boolean = false, def?: T): T | EnhancedArray<T> {
    const conf = JSON.parse(JSON.stringify(this.#ConfigCache))

    const result = path ? lodash.get(conf, path, def) : conf

    if (isArray) {
      if (!Array.isArray(result)) {
        logger.error(`配置路径 ${path} 不是数组类型`)
        return new EnhancedArray<T>(this, [], path)
      }

      return new EnhancedArray<T>(this, result, path)
    }

    return result
  }

  /**
   * @param save 是否立即保存
   */
  set<T> (path: string, value: T, save: boolean): void {
    lodash.set(this.#ConfigCache!, path, value)

    save && this.save()
  }

  /**
   * @description 立即保存配置
   */
  save () {
    try {
      logger.debug(`[${this.#cfgName}] 保存配置`, this.#ConfigCache)

      writeJsonSync(this.#ConfigPath, this.#ConfigCache)
    } catch (err) {
      logger.error(err)
    }
  }

  watch (fnc?: (self: Config<C>, oldData: C, newData: C) => Promise<void> | void) {
    watch(this.#ConfigPath, async (oldData: C, newData) => {
      fnc && await fnc(this, oldData, newData)

      this.loadConfig()
    })

    return this
  }
}
